iT邦幫忙

2024 iThome 鐵人賽

DAY 8
0
JavaScript

TypeScript 初學者也能看的學習指南系列 第 8

TypeScript 初學者也能看的學習指南 08 - Function 函式

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20240917/20149362ZYCXv4zM4g.png

本篇將會了解不同種類的 Function 在 TypeScript 中的寫法和變化

Function 分成幾大部分講解

  1. 函式宣告式
  2. 函式表達式
  3. 箭頭函式
  4. 泛型函式
  5. 完整定義函式型別
  6. 介面函式
  7. 當其餘參數碰到函式

函式宣告式(Function Declaration)

又稱「函式陳述式 (Function Statement)

我們先把函式拆成兩部份來看

  1. 參數(Parameter)
  2. 回傳值(Return Value)

在 TypeScript 中,可以為函式的「參數」和「回傳值」定義型別,若不寫則會被推斷為 any
以下方 test 為例,相加的結果自然也是 any
https://ithelp.ithome.com.tw/upload/images/20240810/20149362rT2ejpKzMU.png

  • 為參數 a, b 加上型別註釋
function add(a: string, b: string) {
  return a + b;
}

add('1', '4') // ✅ pass
add(1, '2') // ❌ Argument of type 'number' is not assignable to parameter of type 'string'. 第一個參數必須要是字串,非數字
  • 若沒有回傳值,可以使用 void; 若有回傳值,則在參數後定義即可,如下例:
function f2(a: number, b: number): void {
  document.querySelector('body').append(document.createElement('div'));
}

// 下方會被自動推斷為 function noop(): void 
function noop() {
  return;
}

function f3(a: number, b: number): number { // 最右邊的 : number 代表回傳值型別
  return a + b;
}

// return 4 為數字,回傳值會自動被推斷為 : number
function getNum(a, b) {
  return 4;
}

若回傳的是 Promise 呢?

假設回傳的是一個 Promise,就必須使用內建的 Promise 型別。如下方的 Promise<number> ,這也是一個泛型(之後章節會談到),代表函式在被呼叫後會回傳 Promise,而 Promise 被解析後會回傳一個型別為 number 的值

⭐️ 補充:Promise 雖是一個物件型別,但在 TypeScript 中,它是一個帶有「泛型參數」的特殊物件型別,專門用來處理非同步。因此,直接將 Promise 定義為 :object 會喪失 Promise 的特殊語義和型別安全性

async function getFavoriteNumber(): Promise<number> {
  return 26;
}
async function fetchData(): Promise<object> {
  return { name: "Hannah", age: 18 };
}

函式表達式(Function Expression)

先來回顧一下 JavaScript

函式表達式的名稱可以是「具名的」或「匿名的」
函式陳述式的名稱必須是「具名的」

這是典型的「函式表達式」

let f3 = function(a: string, b: string): string {
  return a + b;
}

匿名函式(Anonymous Functions)

範例內容取自官方文件

const names = ["Alice", "Bob", "Eve"];
 
names.forEach(function (s) {    // ❓ 猜猜看 s 會被推斷為什麼型別
  console.log(s.toUpperCase());
});

答案是
https://ithelp.ithome.com.tw/upload/images/20240918/20149362ZvC6ceFQNm.png

此匿名函式會在呼叫 forEach 的時候被執行,參數 s 會自動給定類型,而且型別就算沒定義也不會是 any
因為 TypeScript Compiler 會先找到 iterate 的目標對象 names,並依照 names 裡的值去自動推斷出 s 的型別

以上這個過程稱為 「contextual typing」,中文直翻為「情境推斷」,也就是說 函式發生的情境決定了參數應該是什麼型別
這跟「type inference 型別推斷」很類似,多了解一點能幫助你在為來更得心應手的判斷何時可以安心的省略「型別註釋」


箭頭函式 Arrow Functions

let f4 = (a: string, b: string): string => {
  return a + b;
}

Generic Functions 泛型函式

泛型函式就是「泛型」結合「函式」
<T> 是個型別代稱,代表「任意的型別」
此範例接受一個型別為 T 的參數並直接回傳,因此,輸入什麼類型,傳回來的就是什麼類型

function identity<T>(arg: T): T {
    return arg;
}

let output1 = identity<string>('myString');  // string
let output2 = identity<number>(100);         // number
let output3 = identity<number>('100'); // ❌ Argument of type 'string' is not assignable to parameter of type 'number'.

完整定義函式型別

先前都是針對「函式本身」做講解
那完整定義一個變數是「函式型別」會怎麼寫呢?

格式: (參數1: <type1>, 參數2: <type2>...) => <回傳的type>

下方例子中的第一個箭頭(=>)後代表「回傳值的型別」,要注意它並非 ES6 的箭頭函式,別混淆了
可以用等於(=)將函式拆成左右兩邊來看,比較不會眼花 XD

let f4: (a: string, b: string) => string = function(a: string, b: string): string {
  return a + b;
}
let f5: (a: string, b: string) => string = (a: string, b: string): string => {
  return a + b;
}
function sayHi(fn: (a: string) => void) {
  fn("Hello TypeScript");
}
function printToConsole(s: string) {
  console.log(s); // Hello TypeScript
}
 
sayHi(printToConsole);

❌ 前後定義沒有一致,就會報錯
https://ithelp.ithome.com.tw/upload/images/20240810/201493623FrvKRcdUc.png


Interface Functions 介面函式

使用 interface 去定義函式的形狀

interface StrFunc {
    (input: string): string;
}

const repeatStr: strFunc = function(input: string): string {
    return input.repeat(2);
};

console.log(repeatString('hello')); // hellohello

當其餘參數 (...) 碰到函式

ES6 的其餘參數(Rest parameters) 能將剩餘的參數透過 ... 符號將其放入一個陣列中,所以在 TypeScript 中我們可以使用 array [] 型別來定義
...numbers 被轉換為一個元素為數字的陣列

function sum(...numbers: number[]): number {
    return numbers.reduce((a, b) => a + b, 0);
}

console.log(sum(1, 2, 3, 4));  // 10
console.log(sum(10, 20));      // 30

總結

函式的表示方法很多種,文中分解成不同類型的函式去講解,並把函式拆成「參數」、「回傳值」兩部分來看,「參數」還可以搭配 可選屬性,喜歡今天的文章的話可以點個 Like 唷 ^^

大家晚安~開完會繼續趕鐵人賽,快閉眼了 XOX

每天講的內容有推到 github 上喔

References


上一篇
TypeScript 初學者也能看的學習指南 07 - Tuple 元組
下一篇
TypeScript 初學者也能看的學習指南 09 - Function Overloads 函式重載
系列文
TypeScript 初學者也能看的學習指南16
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言